/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gclient;

import java.net.*;
import java.io.*;
import java.util.StringTokenizer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Vector;

/**
 *
 * @author sv
 */
public class TrackerCom {

	public TrackerCom(String ipAddress, int tPort, String tUName, String tUPass, String tUPP) throws UnknownHostException {
		trackerAddress = InetAddress.getByName(ipAddress);
		trackerPort = tPort;
		trackerUserName = tUName;
		trackerPassword = tUPass;
		trackerPassphrase = tUPP;
	}

	public boolean nodeLogin() throws NoSuchAlgorithmException, IOException {
		byte[] Sha1Password = Sha1Digest(trackerPassword);
		byte[] Sha1Passphrase = Sha1Digest(trackerPassphrase);
		try {
			trackerSocket = new Socket(trackerAddress, trackerPort);
			streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
			streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

			// Convert Sha1 password byte[] to Base64 encoding
			String passB64 = new sun.misc.BASE64Encoder().encode(Sha1Password);
			String requestMsg = "0 " + trackerUserName + " " + passB64 + " ";
			byte[] replyMsg = new byte[1024];
			streamToTracker.write(requestMsg.getBytes());
			streamFromTracker.read(replyMsg);
			String reply = new String(replyMsg);

			StringTokenizer st = new StringTokenizer(reply, " ");
			String[] tokens = new String[4];

			int nTokens = 0;
			while (st.hasMoreTokens()) {
				tokens[nTokens++] = st.nextToken();
			}
			// nTokens = Actual No. of tokens + 1 (for the last space filled token)
			switch (nTokens) {
				case 2:
					javax.swing.JOptionPane.showMessageDialog(null, "Incorrect Username/Password!");
					return false;
				case 3:
					String b64PassPhrase = new sun.misc.BASE64Encoder().encode(Sha1Passphrase);
					if (b64PassPhrase.equals(tokens[1])) {
						authenticated = true;
						return true;
					} else {
						javax.swing.JOptionPane.showMessageDialog(null, "Passphrase Mismatch");
						return false;
					}
			}
		} catch (IOException iexp) {
			System.err.println("Error occured: " + iexp.getMessage());
		}
		setErrorMsg("Unknown Login Error");
		return false;
	}

	public void nodeLogout() throws IOException {
		trackerSocket = new Socket(trackerAddress, trackerPort);
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

		String msg = "7 " + trackerUserName + " ";
		streamToTracker.write(msg.getBytes());

		// Close connections
		trackerSocket.close();
		streamToTracker.close();
		streamFromTracker.close();
		authenticated = false;
	}

    public void sendInvite(int gid, String invUName) throws IOException {
        trackerSocket = new Socket(trackerAddress, trackerPort);
        streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
        streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

        String rqMsg = "2 0 " + trackerUserName + " " + invUName + " " + gid + " ";
        streamToTracker.write(rqMsg.getBytes());
    }

	public boolean isLoggedIn() {
		return authenticated;
	}

	public static byte[] Sha1Digest(String msg) throws NoSuchAlgorithmException {
		MessageDigest sha = MessageDigest.getInstance("SHA-1");
		sha.update(msg.getBytes());
		return sha.digest();
	}
    
    public static String B64Encode(byte[] msg) {
        String b64Reply = new sun.misc.BASE64Encoder().encode(msg);
        return b64Reply;
    }

    public static byte[] B4Decode(String msg) throws IOException {
        byte[] b64Reply = new sun.misc.BASE64Decoder().decodeBuffer(msg);
        return b64Reply;
    }

	public int createGroup() throws IOException {
		if (!authenticated) {
			setErrorMsg("User not yet authenticated");
			return -1;
		}
		trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

		String requestMsg = "1 " + trackerUserName + " ";
		byte[] replyMsg = new byte[128];

		streamToTracker.write(requestMsg.getBytes());
		streamFromTracker.read(replyMsg);
		String reply = new String(replyMsg);

		StringTokenizer st = new StringTokenizer(reply, " ");

		st.nextToken();
		String sGID = st.nextToken();
		int groupID = Integer.parseInt(sGID);
		return groupID;
	}

	public void leaveGroup(int groupID) throws IOException {
        trackerSocket = new Socket(trackerAddress, trackerPort);
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

		String requestMsg = "3 " + trackerUserName + " " + groupID + " ";
		streamToTracker.write(requestMsg.getBytes());
	}

	public boolean addFileToGroup(int groupId, String sha1FileName) throws IOException {
        trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

		String requestMsg = "8 " + trackerUserName + " " + sha1FileName + " " + groupId + " ";
		streamToTracker.write(requestMsg.getBytes());

		byte[] replyMsg = new byte[1024];
		streamFromTracker.read(replyMsg);
		String reply = new String(replyMsg);

		StringTokenizer st = new StringTokenizer(reply, " ");

		if ( st.nextToken().equals ( "8" ) )
			if ( st.nextElement().equals("1") )
				return true;

		return false;
	}

	public Vector<String> peerList(int groupId, String sha1FileName) throws IOException {
		String requestMsg = "9 " + trackerUserName + " " + sha1FileName + " " + groupId + " ";
		trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());
		streamToTracker.write(requestMsg.getBytes());

		byte[] replyMsg = new byte[1024];
		streamFromTracker.read(replyMsg);
		String reply = new String(replyMsg);

		StringTokenizer st = new StringTokenizer(reply, " ");

		if ( st.nextToken().equals("9")) {
			if ( st.nextToken().equals("1")) {
				Vector<String> peerList = new Vector<String>();

				while( st.hasMoreTokens() ) {
					peerList.add(st.nextToken());
				}
				
				return peerList;
			}
			else
				return null;
		}

		return null;
	}
	
	public Vector<String> getIPPeerList( int groupId, String sha1FileName) throws IOException {
		Vector<String> pList = peerList(groupId, sha1FileName);
		Vector<String> ipList = new Vector<String>(); 

		for ( String peer : pList ) {
			if ( !( peer.equals(" ") || peer.equals("") ) ) {
				ipList.add(getUserIP(peer));
			}
		}

		return ipList;
	}

    public Vector<String> getUserList(int gid) throws IOException{
        trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

        Vector<String> foo = new Vector<String>();
        String requestMsg = "10 " + trackerUserName + " " + gid + " ";
        streamToTracker.write(requestMsg.getBytes());
        byte[] replyMsg = new byte[32768];
        streamFromTracker.read(replyMsg);
        if(replyMsg.equals("")) return foo;
        
        String sReplyMsg = new String(replyMsg);
        StringTokenizer st = new StringTokenizer(sReplyMsg, " ");

        while(st.hasMoreTokens()) {
            foo.add(st.nextToken());
        }
        foo.removeElementAt(foo.size() - 1);
        return foo;
    }

    public Vector<String> getGroupUserList(int gid) throws IOException {
        trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());

        Vector<String> foo = new Vector<String>();
        String requestMsg = "16 " + trackerUserName + " " + gid + " ";
        streamToTracker.write(requestMsg.getBytes());
        byte[] replyMsg = new byte[32768];
        streamFromTracker.read(replyMsg);

        String sReplyMsg = new String(replyMsg);
	System.out.println("User List for the group : " + sReplyMsg.trim() );
        StringTokenizer st = new StringTokenizer(sReplyMsg, " ");

        int nElements = Integer.parseInt(st.nextToken().trim());
        if(nElements == 0) return foo;
        for (int i = 0; i < nElements; i++) {
            foo.add(st.nextToken());
        }
        return foo;
    }

	public void startDownloadAsync(int groupId,String sha1FileName, String fileName, GalaxiaClientUI galaxiaClientUI) throws IOException {
		Vector<String> peers = peerList(groupId, sha1FileName);
		Vector<String> ipAddressList = new Vector<String>();

		for ( String peer : peers ) {
			if ( getUserStatus(peer) ) {
				ipAddressList.add(getUserIP(peer));
			}
		}

		Downloader dwnldr = new Downloader(ipAddressList,sha1FileName, fileName, groupId, trackerUserName , galaxiaClientUI);
		dwnldr.start();
	}

	public String getUserIP( String userName ) throws IOException {
		String requestMessage = "5 " + userName + " ";

		trackerSocket = new Socket(trackerAddress, trackerPort);
		streamFromTracker = new DataInputStream(trackerSocket.getInputStream());
		streamToTracker = new DataOutputStream(trackerSocket.getOutputStream());
		streamToTracker.write(requestMessage.getBytes());

		byte[] replyMessage = new byte[128];
		streamFromTracker.read(replyMessage);
		String reply = new String(replyMessage);
		StringTokenizer st = new StringTokenizer(reply, " ");

		if ( st.nextToken().equals("5")) {
			if ( st.nextToken().equals("1") ) {
				return st.nextToken();
			}
		}

		return null;
	}

	public boolean getUserStatus(String userName) throws IOException {
		if (getUserIP( userName ).equals(""))
			return false;
		else
			return true;
	}

	public String getErrorMsg() {
		return sErrMsg;
	}

	private void setErrorMsg(String s) {
		sErrMsg = s;
	}

    public String getTrackerUserName() {
        return trackerUserName;
    }
	protected InetAddress trackerAddress;
	protected int trackerPort;
	protected String trackerUserName,  trackerPassword,  trackerPassphrase;
	protected Socket trackerSocket;
	private String sErrMsg;
	private boolean authenticated;
	private DataInputStream streamFromTracker;
	private DataOutputStream streamToTracker;
}
